package com.imooc.miaosha.system.interceptor;

import com.alibaba.fastjson.JSON;
import com.imooc.miaosha.constant.CookieConstant;
import com.imooc.miaosha.domian.User;
import com.imooc.miaosha.redis.RedisFactory;
import com.imooc.miaosha.redis.keys.MiaoshaKey;
import com.imooc.miaosha.result.Result;
import com.imooc.miaosha.result.ResultEnums;
import com.imooc.miaosha.service.UserService;
import com.imooc.miaosha.system.annotation.AccessLimit;
import com.imooc.miaosha.system.data.UserThreadLocalContext;
import com.imooc.miaosha.util.CookieUtils;
import com.imooc.miaosha.util.ServletUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;

/**
 * @Author Mr. xyb
 * @Desc 秒杀限流拦截器
 * @Date 2019/4/25 11:08
 */
@Component
public class AccessLimitInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (ServletUtils.getRequest() == null && ServletUtils.getResponse() == null) {
            ServletUtils.setRequest(request);
            ServletUtils.setResponse(response);
        }
        if (handler instanceof HandlerMethod) {
            User user = getUser(request);
            if (user != null) {
                UserThreadLocalContext.set(user);
            }
            AccessLimit annotation = ((HandlerMethod) handler).getMethodAnnotation(AccessLimit.class);
            if (annotation == null) {
                return true;
            }
            int seconds = annotation.seconds();
            int maxCount = annotation.maxCount();
            boolean needLogin = annotation.needLogin();
            String key = request.getRequestURI();
            if (needLogin) {
                if (user == null) {
                    failPage(response, ResultEnums.SESSION_ERROR);
                    return false;
                }
                key += user.getId();
            }

            if (key.contains("do_miaosha")) {
                String[] split = key.split("/");
                split[2] = "";
                key = split.toString();
            }

            MiaoshaKey access = MiaoshaKey.access;
            access.setExpireSeconds(seconds);

            Integer count = redisFactory.get(MiaoshaKey.access, key, Integer.class);

            if (count == null) {
                redisFactory.set(access, key, 1);
            } else if (count < maxCount) {
                redisFactory.incr(MiaoshaKey.access, key);
            } else {
                failPage(response, ResultEnums.ACCESS_LIMIT_REACHED);
                return false;
            }

        }
        return super.preHandle(request, response, handler);
    }

    private void failPage(HttpServletResponse response, ResultEnums errorMsg) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        OutputStream outputStream = response.getOutputStream();
        String str = JSON.toJSONString(Result.error(errorMsg));
        outputStream.write(str.getBytes("utf-8"));
        outputStream.flush();
        outputStream.close();
    }

    /**
     * 获取用户
     *
     * @param request
     * @return
     */
    private User getUser(HttpServletRequest request) {
        String paramToken = request.getParameter(CookieConstant.COOKIE_NAME_TOKEN);
        String cookieToken = CookieUtils.getCookie(CookieConstant.COOKIE_NAME_TOKEN);
        if (StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {
            return null;
        }
        String token = !StringUtils.isEmpty(paramToken) ? paramToken : cookieToken;
        return userService.getUserByToken(token);
    }

    @Autowired
    private UserService userService;

    @Autowired
    private RedisFactory redisFactory;

}
